﻿/* ***** BEGIN LICENSE BLOCK *****
 *
 * The contents of this file are subject to the Mozilla Public License Version 1.1
 * (the "License"); you may not use this file except in compliance with the
 * License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 * the specific language governing rights and limitations under the License.
 * 
 * The Original Code is CnCNet Client code, Released 2nd January 2009.
 * 
 * The Initial Developers of the Original Code are
 * Adam Krock and John Sanderson. Portions created by
 * the Initial Developer are Copyright (C) 2010
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s): Adam Krock, John Sanderson.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 * 
 * ***** END LICENSE BLOCK *****/

using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Diagnostics;
using System.Reflection;
using System.Threading;
using System.IO;
using Microsoft.Win32;
using System.Net.NetworkInformation;
using System.Runtime.InteropServices;
using System.Drawing.Drawing2D;
using System.Windows;

namespace CnCNet
{
    public partial class cncNetForm : Form
    {

        #region Variables

        // Objects
        public static cncNetXml myXMLSettings = new cncNetXml();
        public static cncNetClientList myClientList = new cncNetClientList();
        public static cncNetServerList myServerList = new cncNetServerList();
        public static cncNetCountryLookup myCountryLookup;
        //public static cncNetUPnP myPortForwarder = new cncNetUPnP(); // John 17/04/2009
        public static cncNetIni myINISettings; // John 17/04/2009
        // Threads
        public static Thread myDatabaseThread;
        public static Thread myUPnPThread;
        // Static Sockets
        public static Socket myDatabaseSocket;
        public static Socket myPortCheckerSocket;// TEST FOR SYNC RECIEVE
        // Networking
        public static IPEndPoint myReceivingEndPoint;
        public static IPAddress myExternalAddress;
        public static IPAddress myInternalAddress;
        public static IPAddress myGatewayAddress;
        public static int myInternalAdressInt;
        public static bool myConnected;
        public static bool myPortForwarded;
        public static bool myUseGeoLocation;
        public static string myClientDetails;
        public static byte[] myListRequest;
        public static int myPortForwardChecks;
        public static ushort myUserPortNetwork;
        public static ushort myUserPortHost;
        public static ushort myDosboxPortNetwork;
        public static ushort myDosboxPortHost;

        // Game variables
        public static string myGamePath = String.Empty; // Just to be different
        public static bool myGameRunning;
        public static bool myReceiveRunning;

        // MyGame
        public static cncNetMyGame myGame;
        public static cncNetMyGameForm myGameForm;

        #endregion


        #region Constructor
        public cncNetForm()
        {
            if (updateClient())
            {
                Environment.Exit(101);
            }
            else
            {
                //tryToForwardPorts(); // John 17/04/2009
                firstRun(); // John 17/04/2009
                InitializeComponent();
                initilizeVariables();
                initilizeObjects();
                initilizeComms();
                connect();
            }
        }

        #endregion


        #region Initialize Program objects

        private void initilizeVariables()
        {
            myConnected = false;
            myPortForwarded = false;
            myPortForwardChecks = 0;
            myGameRunning = false;
            myReceiveRunning = false;
            myUseGeoLocation = false;
            myUserPortNetwork = methods.htnus(myXMLSettings.CNCNET_USER_PORT);
            myUserPortHost = myXMLSettings.CNCNET_USER_PORT;
            myDosboxPortNetwork = methods.htnus(myXMLSettings.CNCNET_USER_PORT);
            myDosboxPortHost = 8054;
        }

        private void initilizeObjects()
        {
            try
            {
                // Game path
                if (myXMLSettings.CNCNET_USER_GAMEPATH.Trim().Length.Equals(0))
                {
                    // REGISTRY (TO LAUNCH C&95)
                    // Attempt to open the key
                    RegistryKey tempOne = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Westwood\\Command & Conquer Windows 95 Edition");
                    if (tempOne != null)
                    {
                        myGamePath = tempOne.GetValue("InstallPath").ToString();
                        ToolStripItem[] tempTwo = cncNetMenu.Items.Find("Launch", true);
                        tempTwo[0].Enabled = true;
                    }
                    else
                    {
                        tempOne = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Wow6432Node\\Westwood\\Command & Conquer Windows 95 Edition");
                        if (tempOne != null)
                        {
                            myGamePath = tempOne.GetValue("InstallPath").ToString();
                            ToolStripItem[] tempTwo = cncNetMenu.Items.Find("Launch", true);
                            tempTwo[0].Enabled = true;
                        }
                    }
                }
                else
                {
                    // Run from path
                    myGamePath = myXMLSettings.CNCNET_USER_GAMEPATH;
                    ToolStripItem[] tempTwo = cncNetMenu.Items.Find("Launch", true);
                    tempTwo[0].Enabled = true;
                }

                // If path is empty add what we might have found.
                if (myXMLSettings.CNCNET_USER_GAMEPATH == "") myXMLSettings.CNCNET_USER_GAMEPATH = '"' + myGamePath + '"';


                // John 17/04/09 - Do stuff with the ini file if there's a path to C&C
                if (myGamePath != "")
                {
                    myINISettings = new cncNetIni(myGamePath);
                    // Extract the username from the ini file and use as login credentials
                    myXMLSettings.CNCNET_USER_NAME = myINISettings.IniReadValue("MultiPlayer", "Handle");
                }

                // Check if the file exists first
                if (File.Exists("GeoIP.dat"))
                {
                    // Initialize geolocation
                    myCountryLookup = new cncNetCountryLookup("GeoIP.dat");
                    // Set variable for program
                    myUseGeoLocation = true;

                }

                if (!myReceiveRunning && myUPnPThread == null)
                {
                    if (File.Exists("Mono.Nat.dll") && myXMLSettings.CNCNET_USER_UPNP)
                    {
                        myUPnPThread = new Thread((ThreadStart)delegate { new cncNetUPnP(); });
                        myUPnPThread.IsBackground = true;
                        myUPnPThread.Start();
                    }
                    else
                    {
                        WebClient wc = new WebClient();
                        wc.DownloadFile(myXMLSettings.CNCNET_USER_UPDATECLIENTHOST + "Mono.Nat.dll",
                                        "Mono.Nat.dll");

                        // 2nd time lucky
                        if (File.Exists("Mono.Nat.dll"))
                        {
                            myUPnPThread = new Thread((ThreadStart)delegate { new cncNetUPnP(); });
                            myUPnPThread.IsBackground = true;
                            myUPnPThread.Start();
                        }
                    }
                }

                // Check if it is set to run at startup
                //checkIfRunOnStartup();

                // My game
                myGame = new cncNetMyGame();
                myGameForm = new cncNetMyGameForm();
            }
            catch (Exception e)
            {
                MessageBox.Show(e.ToString());
            }
        }

        #endregion


        #region Major Events

        /// <summary>
        /// Added by John on 17/04/2009
        /// Does anything needed by a new version
        /// </summary>
        public void firstRun()
        {
            // Changes for 1.2.1.6
            if (myXMLSettings.CNCNET_USER_VERSION != "1.2.1.6")
            {
                // Changes for 1.2.1.0
                if (myXMLSettings.CNCNET_USER_VERSION != "1.2.1.0")
                {
                    // Changes for version 1.2
                    if (myXMLSettings.CNCNET_USER_VERSION != "1.2.0.0")
                    {
                        myXMLSettings.CNCNET_DATABASE_HOST = "http://cncnet.cnc-comm.com/";
                        myXMLSettings.CNCNET_USER_UPDATECLIENTHOST = "http://cncnet.cnc-comm.com/";
                        myXMLSettings.CNCNET_USER_UPDATECLIENTSRC = "http://cncnet.cnc-comm.com/CnCNet.upd";

                        myXMLSettings.CNCNET_USER_VERSION = "1.2.0.0";
                    }
                    myXMLSettings.addXmlElement("user", "upnp");
                    myXMLSettings.CNCNET_USER_UPNP = true;
                    WebClient wc = new WebClient();
                    wc.DownloadFile(myXMLSettings.CNCNET_USER_UPDATECLIENTHOST + "Mono.Nat.dll",
                                    "Mono.Nat.dll");

                    myXMLSettings.CNCNET_USER_VERSION = "1.2.1.0";
                }
                RegistryKey tempOne =
                    Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true);
                if (tempOne.GetValue("CnCNet") != null)
                {
                    // Remove the value from the registry so that the application doesn't start
                    tempOne.DeleteValue("CnCNet", false);
                }

                myXMLSettings.CNCNET_USER_UPNP = false;

                myXMLSettings.CNCNET_USER_VERSION = "1.2.1.6";
            }
        }

        private void getClientInfo()
        {
            try
            {
                myInternalAddress = methods.getInternalIp();
                myInternalAdressInt = methods.IPAddressToInt(myInternalAddress.ToString());

                myClientDetails = "&ip=" + getExternalIp() + "&port=" + myXMLSettings.CNCNET_USER_PORT + "&name=" + myXMLSettings.CNCNET_USER_NAME;

                // Get Gateway IP address
                // Enumerate all network devices
                foreach (NetworkInterface a in NetworkInterface.GetAllNetworkInterfaces())
                {
                    // Enumerate all IP addresses on device
                    foreach (UnicastIPAddressInformation b in a.GetIPProperties().UnicastAddresses)
                    {
                        // If internal Address is equal the device address, this is the one we want
                        if (myInternalAddress.Equals(b.Address))
                        {
                            // Get the topmost gateway device
                            myGatewayAddress = a.GetIPProperties().GatewayAddresses[0].Address;
                        }
                    }
                }
            }
            catch (Exception e)
            {
                debug("getClientInfo()-" + e.Message);
            }
        }

        public bool updateClient()
        {
            try
            {
                // Close all occurances of CNCNET
                foreach (Process Proc in Process.GetProcessesByName(myXMLSettings.CNCNET_USER_UPDATECLIENTDST))
                {
                    Proc.Kill();
                    Proc.WaitForExit();
                }

                // Go to the host and check for the latest version
                WebClient wc = new WebClient();
                Stream myStream = wc.OpenRead(myXMLSettings.CNCNET_USER_UPDATECLIENTHOST + "version.txt");
                StreamReader sr = new StreamReader(myStream);

                // Save/initialize version variables
                string newVersion = sr.ReadLine().Trim();
                int newVersionContents = Int32.Parse(newVersion.Replace(".", ""));
                int oldVersionContents = Int32.Parse(getCurrentAssemblyVersion().Replace(".", ""));
                myStream.Close();


                // Compare the versions
                if (newVersionContents > oldVersionContents)
                {
                    wc.DownloadFile(myXMLSettings.CNCNET_USER_UPDATECLIENTSRC,
                                    myXMLSettings.CNCNET_USER_UPDATECLIENTDST);
                    string target = Application.ExecutablePath;
                    target = target.Remove(target.LastIndexOf('.'), 4);
                    target += ".upd";
                    File.Delete(Application.ExecutablePath + ".bak");
                    File.Move(Application.ExecutablePath, Application.ExecutablePath + ".bak");
                    File.Move(target, Application.ExecutablePath);
                    MessageBox.Show("Client has been updated to [" + newVersion + "] please restart client", "Update Complete");
                    return true;
                }
                return false;
            }
            catch (Exception e)
            {
                MessageBox.Show("Net/Server error, unable to update");
                Environment.Exit(101);
                debug("Update Error: " + e.Message);
                return false;
            }
        }

        public void initilizeComms()
        {
            try
            {


                // Initialize Sockets
                // TCP Socket
                myDatabaseSocket = new Socket(AddressFamily.InterNetwork,
                                                        SocketType.Stream,
                                                        ProtocolType.Tcp);

                // Get client info
                getClientInfo();


            }
            catch (Exception e)
            {
                debug("initilizeComms()-" + e.Message);
            }
        }

        public void reconnect()
        {
            disconnect();
            connect();
        }

        public void connect()
        {
            try
            {
                myExternalAddress = IPAddress.Parse(getExternalIp()); //More Accurate

                myDatabaseSocket = new Socket(AddressFamily.InterNetwork,
                                                        SocketType.Stream,
                                                        ProtocolType.Tcp);

                myDatabaseThread = new Thread(new ThreadStart(databaseWorkerRun));
                myDatabaseThread.Start();

                myReceiveWorkerRun();
            }
            catch (Exception e)
            {
                debug("connect()-" + e.Message);
            }
        }

        public void disconnect()
        {
            try
            {
                myConnected = false;
                myReceiveRunning = false;
                // Close database connection + thread
                myDatabaseSocket.Close();
                myDatabaseThread.Abort();
                // Set icon
                cncNetTray.Icon = new Icon(GetType(), "disconnected.ico");
            }
            catch (Exception e)
            {
                debug("disconnect()-" + e.Message);
            }
        }

        #endregion


        #region Worker Methods

        // Updates client list
        private void databaseWorkerRun()
        {
            int loopCount = 0;

            myConnected = true;
            cncNetTray.Icon = new Icon(GetType(), "connected.ico");

            while (myConnected)
            {
                try
                {
                    // used to build entire input
                    StringBuilder sb = new StringBuilder();

                    // used on each read operation
                    byte[] buf = new byte[8192];

                    // prepare the web page we will be asking for
                    HttpWebRequest request = (HttpWebRequest)
                        WebRequest.Create(myXMLSettings.CNCNET_DATABASE_HOST + "cncnet.php?action=add" + myClientDetails);

                    // execute the request
                    HttpWebResponse response = (HttpWebResponse)
                        request.GetResponse();


                    if (!response.StatusCode.Equals(HttpStatusCode.OK))
                    {
                        // For 400 and 500 responses:
                        cncNetTray.Icon = new Icon(GetType(), "disconnected.ico");
                        cncNetTray.Text = "Disconnected!";
                        //myConnected = false;
                    }
                    else // We're online!
                    {
                        // we will read data via the response stream
                        Stream resStream = response.GetResponseStream();

                        string tempString = null;
                        int count = 0;

                        do
                        {
                            // fill the buffer with data
                            count = resStream.Read(buf, 0, buf.Length);

                            // make sure we read some data
                            if (count != 0)
                            {
                                // translate from bytes to ASCII text
                                tempString = Encoding.ASCII.GetString(buf, 0, count);

                                // continue building the string
                                sb.Append(tempString);
                            }
                        }
                        while (count > 0); // any more data to read?

                        // Add player/game count to info
                        myClientList.fillList(sb.ToString());

                        // Perform out of game actions
                        // Check if game is running
                        //if (checkGameisRunning("C&C95") == false)
                        //{
                        // Send broadcast to all clients
                        sendBroadcast();
                        //sendPacketToClients(testPacket);
                        // Check if receiving thread is running
                        if (!myReceiveRunning)
                        {
                            // If so, start thread
                            myReceiveWorkerRun();
                        }
                        //}
                        cncNetTray.Text = "Players: " + myClientList.Count + " (" + myClientList.getActiveUsers() + ")" +
                            " Games: " + myServerList.Count; // John 17/04/09   

                        if (loopCount < 25)
                            loopCount++;
                        else if (loopCount == 25)
                        {
                            if (myClientList.Count > 2 && myClientList.getActiveUsers() == 0)
                            {
                                alert("Possible NAT Error", "You don't appear to be receiving any data from " +
                                    "other users on the network. This could mean that your router is not set up " +
                                    "correctly for use with CnCNet. Check the guide on the CnCNet website to ensure " +
                                    "you've forwarded your ports correctly and disabled any settings that may cause " +
                                    "a problem.");
                            }
                            loopCount = 0;
                        }

                        Thread.Sleep(myXMLSettings.CNCNET_USER_UPDATERATE);
                    }
                }
                catch (Exception e)
                {
                    debug("databaseWorkerRun()-" + e.Message);
                }
            }
        }

        // Intercepts incoming/outgoing packets
        private void myReceiveWorkerRun()
        {
            try
            {
                    // Set Sending Socket
                    Socket mySendingSocket = new Socket(AddressFamily.InterNetwork,
                                                            SocketType.Dgram,
                                                            ProtocolType.Udp);


                    // Set recieving Socket
                    Socket myRecievingSocket = new Socket(AddressFamily.InterNetwork,
                                                            SocketType.Raw,
                                                            ProtocolType.Udp);



                // Bind the socket to the selected IP address
                myRecievingSocket.ReceiveBufferSize = myXMLSettings.CNCNET_USER_BUFFERSIZE;
                myRecievingSocket.Bind(new IPEndPoint(myInternalAddress, myUserPortHost));
                myRecievingSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.HeaderIncluded, true);

                // Set to intercept outgoing
                byte[] byTrue = new byte[4] { 1, 0, 0, 0 };
                byte[] byOut = new byte[4] { 1, 0, 0, 0 };

                try
                {
                    //Socket.IOControl is analogous to the WSAIoctl method of Winsock 2
                    myRecievingSocket.IOControl(IOControlCode.ReceiveAll, byTrue, byOut);

                    // Set running to true
                    myReceiveRunning = true;
                }
                catch (IOException)
                {
                    alert("Connection Error", "Unfortunately it appears that you are using an incompatible wireless card." + 
                        " If possible, you should switch to another wireless adapter or a wired connection. You could try" +
                        " updating your wireless card drivers, which may fix the issue. If updating the drivers doesn't work," +
                        " there is unfortunately no way for you to play CnCNet using your wireless adapter.");
                    disconnect();
                }


                // Create Async object
                cncNetMessage tempOne = new cncNetMessage(mySendingSocket,
                                                            myRecievingSocket,
                                                            myXMLSettings.CNCNET_USER_BUFFERSIZE,
                                                            new IPEndPoint(myInternalAddress, myXMLSettings.CNCNET_USER_PORT));

                // Set buffer for recieving
                myRecievingSocket.BeginReceive(tempOne.myBuffer, 0, tempOne.myBuffer.Length, 0,
                                                    new AsyncCallback(onReceive), tempOne);



            }
            catch (Exception e)
            {
                debug("myReceiveWorkerRun:" + e.Message);
            }
        }

        // Processes intercepted packets
        private void onReceive(IAsyncResult ar)
        {
            try
            {
                cncNetMessage tempOne = (cncNetMessage)ar.AsyncState;
                tempOne.myRecievingSocket.EndReceive(ar);

                // Check if receive is already running
                if (myReceiveRunning)
                {
                    // Check if C&C95 Packet 
                    if (tempOne.myBuffer[29].Equals(52) && tempOne.myBuffer[30].Equals(18))
                    {
                        // Animate
                        receiving();
                        // Decode Packet details
                        cncNetcodec.packets.decode.rawCncPacket myLastCncPacket =
                            new cncNetcodec.packets.decode.rawCncPacket(tempOne.myBuffer);

                        // Filter out fake packets (packet number 0)
                        // DO NOT MESS WITH THIS, UNLESS YOU WANT DISASTER
                        // John - Well, I messed with it. Things turned out better than expected.
                        if (myLastCncPacket.CNCpacketNumber != 0)
                        {
                            // there is no real documentation about the packets so I needed this... :/ --psde
                            /*
                            if (myLastCncPacket.dataType != 0 &&
                                myLastCncPacket.dataType != 1)
                            {
                                StreamWriter SW;
                                SW = File.AppendText("C:\\cnc.txt");
                                SW.WriteLine("IP dest: " + myLastCncPacket.IPDestAddr.ToString());
                                SW.WriteLine("IP source: " + myLastCncPacket.IPSrcAddr.ToString());
                                SW.WriteLine("Type: " + myLastCncPacket.dataType.ToString());
                                SW.WriteLine("Sender: " + myLastCncPacket.dataSender);
                                SW.WriteLine("Data: " + myLastCncPacket.dataMessage.ToString());
                                SW.WriteLine("" + myLastCncPacket.dataPackOne);
                                SW.WriteLine("" + myLastCncPacket.dataPackTwo);
                                SW.WriteLine("" + myLastCncPacket.dataPackEnd);
                                SW.WriteLine("");
                                SW.Close();
                            }*/

                            lock (myGame)
                            {
                                string sender = myLastCncPacket.dataSender.Split(new char[] { (char)0x00 })[0].Trim();
                                switch (myLastCncPacket.dataType)
                                {
                                    case 4:
                                        bool insert = true;
                                        for (int i = myGame.players.Count - 1; i >= 0; i--)
                                        {
                                            string player = myGame.players[i] as string;
                                            if (player == sender)
                                            {
                                                insert = false;
                                            }
                                        }

                                        if (insert)
                                        {
                                            myGame.chatlog += "-!- " + sender + " joined your game.\r\n";
                                            myGame.players.Add(sender);
                                        }

                                        break;

                                    case 8:
                                        bool removed = false;
                                        for (int i = myGame.players.Count - 1; i >= 0; i--)
                                        {
                                            string player = myGame.players[i] as string;
                                            if (player == sender)
                                            {
                                                myGame.players.Remove(myGame.players[i]);
                                                removed = true;
                                            }
                                        }
                                        if (removed)
                                            myGame.chatlog += "-!- " + sender + " left your game.\r\n";

                                        break;

                                    case 10:
                                        string message = myLastCncPacket.dataMessage.Split(new char[] { (char)0x00 })[0].Trim();
                                        myGame.chatlog += sender + ": " + message + "\r\n";
                                        break;
                                }
                            }

                            //myGameForm.refresh();

                            // Outgoing REAL packets
                            if (myLastCncPacket.IPSrcAddr.Equals(myInternalAdressInt))
                            {
                                #region Broadcast Packets
                                // Check if a broadcast packet
                                if (myLastCncPacket.IPDestAddr.Equals(-1))
                                {
                                    // Filter by packet type
                                    // Accounting for all documented/possible types
                                    switch (myLastCncPacket.dataType)
                                    {
                                        // Server discovery broadcast
                                        case 0:
                                            // Get data
                                            cncNetcodec.packets.decode.cncData broadcastOneRelay =
                                                new cncNetcodec.packets.decode.cncData(tempOne.myBuffer,
                                                    methods.htnus(myLastCncPacket.UDPLength));
                                            // Forward to all clients
                                            foreach (cncNetClient a in myClientList.ToArray())
                                            {
                                                Socket tempSocket = new Socket(AddressFamily.InterNetwork,
                                                                                SocketType.Dgram,
                                                                                ProtocolType.Udp);
                                                //tempSocket.Ttl = 255;
                                                //tempSocket.Bind(new IPEndPoint(myInternalAddress, myUserPortHost));
                                                tempSocket.SendTo(broadcastOneRelay.packetData, a.myIPEndPoint);
                                                tempSocket.Close();
                                            }

                                            break;
                                        // Player discovery broadcast
                                        case 2:
                                            // Get data
                                            cncNetcodec.packets.decode.cncData broadcastTwoRelay =
                                                new cncNetcodec.packets.decode.cncData(tempOne.myBuffer,
                                                    methods.htnus(myLastCncPacket.UDPLength));
                                            // Forward to all clients
                                            foreach (cncNetClient a in myClientList.ToArray())
                                            {
                                                // Send to client
                                                Socket tempSocket = new Socket(AddressFamily.InterNetwork,
                                                                                SocketType.Dgram,
                                                                                ProtocolType.Udp);
                                                //tempSocket.Ttl = 255;
                                                //tempSocket.Bind(new IPEndPoint(myInternalAddress, myUserPortHost));
                                                tempSocket.SendTo(broadcastTwoRelay.packetData, a.myIPEndPoint);
                                                tempSocket.Close();
                                            }

                                            break;
                                        // Player leaving broadcast
                                        case 8:
                                            break;
                                        // Player message to all broadcast
                                        case 10:
                                            break;
                                    }
                                }
                                #endregion

                                #region Non-Broadcast Packets
                                else
                                {

                                    // If Not a broadcast
                                    switch (myLastCncPacket.dataType)
                                    {
                                        case 1:
                                            break;
                                        case 2:
                                            break;
                                        case 3:
                                            break;
                                        case 4:
                                            break;
                                        case 5:
                                            break;
                                        case 6:
                                            break;
                                        case 7:
                                            break;
                                        case 8:
                                            break;
                                        case 9:
                                            break;
                                        case 10:
                                            break;
                                        case 11:
                                            break;
                                    }
                                }
                                #endregion
                            }

                            // Incoming REAL packets
                            if (myLastCncPacket.IPDestAddr.Equals(myInternalAdressInt))
                            {
                                #region Broadcast Packets
                                // Check if a broadcast packet
                                if (myLastCncPacket.IPDestAddr.Equals(-1))
                                {
                                    // Filter by packet type
                                    // Accounting for all documented/possible broadcasts
                                    switch (myLastCncPacket.dataType)
                                    {
                                        // Server check
                                        case 0:
                                            break;
                                        // Player check
                                        case 2:
                                            break;
                                        // Leave packet
                                        case 8:
                                            break;
                                        // Message to all
                                        case 10:
                                            break;
                                    }
                                }
                                #endregion

                                #region Non-broadcast Packets
                                else
                                {
                                    // If Not a broadcast
                                    switch (myLastCncPacket.dataType)
                                    {
                                        // Broadcast response
                                        case 1:
                                            // Server Registration
                                            string serverName = myLastCncPacket.dataSender.Split(new char[] { (char)0x00 })[0].Trim();
                                            if (myServerList.addServer(serverName))
                                            {
                                                string countryName = getCountryOfIP(myLastCncPacket.IPSrcAddr);
                                                alert("Game Hosted!", countryName + serverName + " has hosted a game");
                                            }
                                            else
                                            {
                                                myServerList.getServer(serverName).reset();
                                            }
                                            break;
                                        // Join request
                                        /*case 4:
                                            // alert user of person joining
                                            string joinUserName = myLastCncPacket.dataSender.Split(new char[] { (char)0x00 })[0].Trim();
                                            alert("User Joined!", joinUserName + " has joined your game");
                                            // TODO: Send joining user the country of the server
                                            break;*/
                                    }
                                }
                                #endregion
                            }
                        }
                        else
                        {
                            // Outgoing FAKE packets
                            if (myLastCncPacket.IPSrcAddr.Equals(myInternalAdressInt))
                            {
                                #region Broadcast Packets
                                // Check if a broadcast packet
                                if (myLastCncPacket.IPDestAddr.Equals(-1))
                                {
                                    // Filter by packet type
                                    // Accounting for all documented/possible types
                                    switch (myLastCncPacket.dataType)
                                    {
                                    }
                                }
                                #endregion

                                #region Non-Broadcast Packets
                                else
                                {
                                    // If Not a broadcast
                                    switch (myLastCncPacket.dataType)
                                    {
                                    }
                                }
                                #endregion
                            }

                            // Incoming FAKE packets
                            if (myLastCncPacket.IPDestAddr.Equals(myInternalAdressInt))
                            {
                                #region Broadcast Packets
                                // Check if a broadcast packet
                                if (myLastCncPacket.IPDestAddr.Equals(-1))
                                {
                                    // Filter by packet type
                                    // Accounting for all documented/possible broadcasts
                                    switch (myLastCncPacket.dataType)
                                    {
                                    }
                                }
                                #endregion

                                #region Non-broadcast Packets
                                else
                                {
                                    // If Not a broadcast
                                    switch (myLastCncPacket.dataType)
                                    {
                                        case 0:
                                            // Wait for broadcasts from other clients
                                            // Activate user if in list
                                            lock (myClientList)
                                            {
                                                foreach (cncNetClient a in myClientList)
                                                {
                                                    if (a.myIPint.Equals(myLastCncPacket.IPSrcAddr))
                                                    {
                                                        a.isActive = true;
                                                        TimeSpan ts = DateTime.Now - a.myBroadcastSended;
                                                        a.myPing = ts.Milliseconds / 2;
                                                        a.myBroadcastSended = DateTime.Now;
                                                    }
                                                }
                                            }
                                            break;
                                    }
                                }
                                #endregion
                            }
                        }
                    }// End C&C95 check		

                    // Reset buffer
                    tempOne.resetBuffer();
                    // Loop
                    tempOne.myRecievingSocket.BeginReceive(tempOne.myBuffer, 0, tempOne.myBuffer.Length, SocketFlags.None,
                                                                new AsyncCallback(onReceive), tempOne);
                }// End check of receive running


            }// End
            catch (Exception e)
            {
                debug("OnReceive:" + e.Message);
                myReceiveRunning = false;
            }
        }



        #endregion


        #region Communication methods


        private void sendBroadcast()
        {
            try
            {
                byte[] tempOne = new cncNetcodec.packets.broadCastOne(0).getBytes();
                foreach (cncNetClient a in myClientList.ToArray())
                {
                    Socket tempThree = new Socket(AddressFamily.InterNetwork,
                                                SocketType.Dgram,
                                                ProtocolType.Udp);
                    tempThree.Ttl = 255;
                    tempThree.Bind(new IPEndPoint(myInternalAddress, myUserPortHost));
                    tempThree.SendTo(tempOne, a.myIPEndPoint);
                    tempThree.Close();
                    a.myBroadcastSended = DateTime.Now;
                }
            }
            catch (Exception e)
            {
                debug("sendPacketToClients()-" + e.Message);
            }
        }


        #endregion


        #region Delegates/Helper Methods


        // Delegates	


        // Helpers

        /*public void checkIfRunOnStartup()
        {
            // Check if startup is enabled
            RegistryKey tempOne =
                Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true);

            if (tempOne.GetValue("CnCNet") == null)
            {
                Startup.Checked = false;
            }
            else
            {
                Startup.Checked = true;
            }
            
        }*/

        /*public void setRunOnStartup()
        {
            // Check if startup is enabled
            RegistryKey tempOne =
                Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true);
            if (Startup.Checked == false)
            {
                // Add the value in the registry so that the application runs at startup
                tempOne.SetValue("CnCNet", Application.ExecutablePath.ToString());
            }
            else
            {
                // Remove the value from the registry so that the application doesn't start
                tempOne.DeleteValue("CnCNet", false);
            }
        }*/

        public bool checkGameisRunning(string game)
        {
            foreach (Process a in Process.GetProcesses())
            {
                if (a.ProcessName.Contains(game))
                {
                    return true;
                }
            }
            return false;
        }

        public void launchGame()
        {
            try
            {
                /*
                // John 17/04/2009 - Sets the online settings to conquer.ini
                if (myGamePath != "")
                {
                    myINISettings.setVarsForOnline();
                    if (myINISettings.getOriginalVars() != cncNetIni.onlineVars)
                    {
                        myXMLSettings.CNCNET_USER_CONQUER = myINISettings.getOriginalVars();
                    }
                }
                */

                Process tempOne = new Process();
                tempOne.StartInfo.Arguments = "-lan";
                tempOne.StartInfo.FileName = myGamePath;
                tempOne.Start();
            }
            catch (Exception e)
            {
                MessageBox.Show("Invalid C&C95 directory, please specify in options dialog");
                debug("Game Launch Error: " + e.Message);
            }
        }

        public void closeApplication()
        {
            /*
            // John 17/04/2009 - Set the ini vars back to normal if changed
            if (myGamePath != "")
            {
                if (myXMLSettings.CNCNET_USER_CONQUER != "")
                {
                    myINISettings.setOriginalVars(myXMLSettings.CNCNET_USER_CONQUER);
                    myXMLSettings.CNCNET_USER_CONQUER = "";
                }
            }
            */

            disconnect();
            this.Hide();
            this.Dispose(true);
            Environment.Exit(1);
        }

        public static string getIpFromInt(int i)
        {
            return new IPAddress(BitConverter.GetBytes(i)).ToString();
        }

        public static string getCountryOfIP(int ip)
        {
            try
            {
                if (myUseGeoLocation)
                {
                    string ipAddress = "Unknown";
                    ipAddress = new IPAddress(BitConverter.GetBytes(ip)).ToString();
                    IPAddress tempTwo = IPAddress.Parse(ipAddress);
                    return "[" + myCountryLookup.lookupCountryName(tempTwo.ToString()) + "] ";
                }
                return "";
            }
            catch (Exception)
            {
                return "";
            }
        }

        public string getCurrentAssemblyVersion()
        {
            Assembly tempOne = Assembly.Load(Assembly.GetExecutingAssembly().FullName);
            string tempTwo = string.Empty;
            foreach (Attribute a in tempOne.GetCustomAttributes(true))
            {
                if (a is AssemblyConfigurationAttribute)
                {
                    tempTwo = (a as AssemblyConfigurationAttribute).Configuration.Trim();
                }
            }
            return tempTwo;
        }

        // TODO:Implement error sending
        public void debug(string s)
        {
            if (myXMLSettings.CNCNET_USER_DEBUG)
            {
                cncNetTray.ShowBalloonTip(1500, "Debug", s, ToolTipIcon.Info);
            }

        }

        public void alert(string title, string text)
        {
            cncNetTray.ShowBalloonTip(1500, title, text, ToolTipIcon.Info);
        }

        public void receiving()
        {
            cncNetTray.Icon = new Icon(GetType(), "recieving.ico");
            cncNetTray.Icon = new Icon(GetType(), "connected.ico");
        }

        #endregion


        #region Events
        private void cncNetForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            //closeApplication();
        }

        private void cncNetTray_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            /*// If currently connected, disconnect
            if (!myConnected)
            {
                connect();
            }
            else
            {
                disconnect();
                Thread.Sleep(1000);
                cncNetTray.Icon = new Icon(GetType(), "disconnected.ico");
            }*/
        }

        private void cncNetMenu_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            ToolStripItem tempOne = (ToolStripItem)e.ClickedItem;
            switch (tempOne.Name)
            {
                case "Startup":
                    //setRunOnStartup();
                    break;
                case "Exit":
                    closeApplication();
                    break;
                case "Chat":
                    Process.Start("http://www.cnc-comm.com/community/chat/index.php");
                    break;
                case "Forums":
                    Process.Start("http://www.cnc-comm.com/community/index.php?board=71.0");
                    break;
                case "Forward":
                    Process.Start("http://cncnet.cnc-comm.com/?page=setup#step4a");
                    break;
                case "How":
                    Process.Start("http://cncnet.cnc-comm.com/?page=setup");
                    break;
                case "Launch":
                    launchGame();
                    break;
                case "Specify":
                    FolderBrowserDialog pathFinder = new FolderBrowserDialog();
                    pathFinder.ShowDialog();
                    if (pathFinder.SelectedPath != "")
                    {
                        myXMLSettings.CNCNET_USER_GAMEPATH = '"' + pathFinder.SelectedPath + "\\C&C95.exe" + '"';
                        myGamePath = myXMLSettings.CNCNET_USER_GAMEPATH;
                    }
                    break;
            }
        }

        private void cncNetMenu_Opened(object sender, EventArgs e)
        {
            // Set the forwarded access state
            ToolStripItem[] tempOne = cncNetMenu.Items.Find("Forward", true);
            tempOne[0].Enabled = !myPortForwarded;
            /*tempOne = cncNetMenu.Items.Find("IpE", true);
            tempOne[0].Text = "External IP: " + myExternalAddress;
            tempOne[0].Enabled = false;
            tempOne = cncNetMenu.Items.Find("IpI", true);
            tempOne[0].Text = "Internal IP: " + myInternalAddress;
            tempOne[0].Enabled = false;
            tempOne = cncNetMenu.Items.Find("IpG", true);
            tempOne[0].Text = "Gateway IP: " + myGatewayAddress;
            tempOne[0].Enabled = false;*/
            //checkIfRunOnStartup();
        }


        #region Hiding Form
        private void cncNetForm_Load(object sender, EventArgs e)
        {
            // Hide form from alt-tab/task bar/etc..   
            FormBorderStyle = FormBorderStyle.None;
            Visible = false;
            ShowInTaskbar = false;
            this.Hide();
        }
        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams cp = base.CreateParams;
                // turn on WS_EX_TOOLWINDOW style bit
                cp.ExStyle |= 0x80;
                return cp;
            }
        }
        #endregion



        #endregion

        private string getExternalIp()
        {
            // used to build entire input
            StringBuilder sb = new StringBuilder();

            // used on each read operation
            byte[] buf = new byte[8192];

            // prepare the web page we will be asking for
            HttpWebRequest request = (HttpWebRequest)
                WebRequest.Create(myXMLSettings.CNCNET_USER_EXTERNALIP);

            // execute the request
            HttpWebResponse response = (HttpWebResponse)
                request.GetResponse();

            // we will read data via the response stream
            Stream resStream = response.GetResponseStream();

            string tempString = null;
            int count = 0;

            do
            {
                // fill the buffer with data
                count = resStream.Read(buf, 0, buf.Length);

                // make sure we read some data
                if (count != 0)
                {
                    // translate from bytes to ASCII text
                    tempString = Encoding.ASCII.GetString(buf, 0, count);

                    // continue building the string
                    sb.Append(tempString);
                }
            }
            while (count > 0); // any more data to read?

            // print out page source
            return sb.ToString();
        }

        private void cncNetForm_Shown(object sender, EventArgs e)
        {
            try
            {
                // Set addresses
                if (!myInternalAddress.ToString().Equals(string.Empty))
                    lblInternalIP.Text = "Internal IP: " + myInternalAddress.ToString();
                if (!myExternalAddress.ToString().Equals(string.Empty))
                    lblExternalIP.Text = "External IP: " + myExternalAddress.ToString();
                if (!myGatewayAddress.ToString().Equals(string.Empty))
                    lblGatewayIP.Text = "Gateway IP: " + myGatewayAddress.ToString();

                // Set game path
                tbxPath.Text = myXMLSettings.CNCNET_USER_GAMEPATH;

                // Set nickname
                tbxNickname.Text = myXMLSettings.CNCNET_USER_NAME;

                if (myXMLSettings.CNCNET_USER_UPNP)
                    radPortYes.Checked = true;
                else
                    radPortNo.Checked = true;
            }
            catch (Exception ex)
            {
                debug("Error showing form: " + ex.Message);
            }
        }

        [DllImport("dwmapi.dll")]
        static extern void DwmIsCompositionEnabled(ref bool pfEnabled);
        [DllImport("dwmapi.dll")]
        static extern void DwmExtendFrameIntoClientArea(IntPtr hWnd, ref Margins pMargins);

        public struct Margins
        {
            public int Left;
            public int Right;
            public int Top;
            public int Bottom;
        }

        private static int glassHeight = 60;

        private void Options_Click(object sender, EventArgs e)
        {
            FormBorderStyle = FormBorderStyle.FixedSingle;
            Visible = true;
            ShowInTaskbar = true;

            // Aero Glass
            this.RecreateHandle();
            if (Environment.OSVersion.Version.Major >= 6)
            {

                bool isGlassSupported = false;
                DwmIsCompositionEnabled(ref isGlassSupported);
                if (isGlassSupported)
                {
                    Margins marg;
                    marg.Top = glassHeight;
                    marg.Left = 0;
                    marg.Right = 0;
                    marg.Bottom = 0;
                    pictureBox1.BackColor = Color.Black;
                    DwmExtendFrameIntoClientArea(this.Handle, ref marg);
                }
            }
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            this.Hide();
        }

        private void tbxPath_Enter(object sender, EventArgs e)
        {
            FolderBrowserDialog pathFinder = new FolderBrowserDialog();
            pathFinder.ShowDialog();
            if (pathFinder.SelectedPath != "")
            {
                tbxPath.Text = pathFinder.SelectedPath;
            }
        }

        private void btnOK_Click(object sender, EventArgs e)
        {
            // Set game path
            if (!tbxPath.Text.Equals(String.Empty))
            {
                if (tbxPath.Text.StartsWith("\"") && tbxPath.Text.EndsWith("C&C95.exe\""))
                    myXMLSettings.CNCNET_USER_GAMEPATH = tbxPath.Text;
                else if (tbxPath.Text.StartsWith("\"") && tbxPath.Text.EndsWith("\""))
                    myXMLSettings.CNCNET_USER_GAMEPATH = tbxPath.Text.Substring(0, (tbxPath.Text.Length - 1)) + "\\C&C95.exe" + '"';
                else
                    myXMLSettings.CNCNET_USER_GAMEPATH = '"' + tbxPath.Text + "\\C&C95.exe" + '"';

                try
                {
                    myINISettings = new cncNetIni(myGamePath);

                    myINISettings.IniWriteValue("MultiPlayer", "Handle", this.tbxNickname.Text);
                }
                catch (Exception except)
                {
                    MessageBox.Show(except.ToString());
                }
            }

            myXMLSettings.CNCNET_USER_UPNP = radPortYes.Checked;

            this.Hide();
            initilizeObjects();
            getClientInfo();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            /*cncNetUsers a = new cncNetUsers();
            a.Show();*/

            // The super secret test button is currently unused.
        }

        private void update_Click(object sender, EventArgs e)
        {
            if (updateClient())
            {
                Environment.Exit(101);
            }
            else
            {
                MessageBox.Show("No updates found!");
            }
        }

        private void online_Click(object sender, EventArgs e)
        {
            cncNetUsers a = new cncNetUsers();
            a.Show();
        }

        private void homepage_Click(object sender, EventArgs e)
        {
            Process.Start("http://cncnet.cnc-comm.com/");
        }

        private void cncNetTray_MouseClick(object sender, MouseEventArgs e)
        {
            if(e.Button == MouseButtons.Left)
                cncNetMenu.Show(this.PointToClient(Cursor.Position));
        }

        // Make the window move when client-area glass is dragged - NOT WORKING.
        /*// make windows do the work for us by lieing to it about where the user clicked
        protected override void WndProc(ref Message m)
        {
            base.WndProc(ref m);

            if (m.Msg == 0x201) // if this is a click
            {
                if (m.Result.ToInt32() == 1) // ...and it is on the client
                {
                    if (this.IsOnGlass(m.LParam.ToInt32())) // ...and specifically in the glass area
                    {
                        m.Result = new IntPtr(2); // lie and say they clicked on the title bar
                    }
                }
            }
        }

        private bool IsOnGlass(int lParam)
        {
            // get screen coordinates
            int x = (lParam << 16) >> 16; // lo order word
            int y = lParam >> 16; // hi order word

            // translate screen coordinates to client area
            Point p = this.PointToClient(new Point(x, y));

            // work out if point clicked is on glass
            lblExternalIP.Text = "[" + y + "]";
            if (y < glassHeight)
                return true;

            return false;
        }*/

        protected override bool ProcessCmdKey(ref System.Windows.Forms.Message msg, System.Windows.Forms.Keys keyData)
        {
            try
            {
                if (msg.WParam.ToInt32() == (int)Keys.Escape)
                {
                    this.Hide();
                }
                else
                {
                    return base.ProcessCmdKey(ref msg, keyData);
                }
            }
            catch (Exception Ex )
            {
                debug("Key Overrided Events Error: " + Ex.Message);
            }
            return base.ProcessCmdKey(ref msg,keyData);
        }

        protected override void OnClosing(CancelEventArgs e)
        {
            if (!this.Visible)
            {
                e.Cancel = false;
                this.closeApplication();
            }
            else
            {
                //Method overridden so we can minimize the app instead of closing it
                e.Cancel = true;
                this.Hide();
            }
        }

        private void about_Click(object sender, EventArgs e)
        {
            cncNetAboutBox a = new cncNetAboutBox();
            a.Show();
        }

        private void myGameToolStripMenuItem_Click(object sender, EventArgs e)
        {
            myGameForm.Show();
        }

    }
}